package main

import (
	"bytes"
	"compress/zlib"
	"io"
	"strconv"
)

const fileFilter = "EBK2 File(*.ebk2);;All File(*)"

type Root = Ebk2Book

type Ebk2Book struct {
	Header     Ebk2Header
	HeaderData Ebk2HeaderData
	BlockData  Ebk2BlockData
}

func (v *Ebk2Book) Unmarshal(dec *Decoder, info *ValueInfo) {
	dec.SetArg(v)
	dec.Decode(&v.Header, &ValueInfo{name: "Header"})
	dec.Decode(&v.HeaderData, &ValueInfo{name: "HeaderData"})
	dec.Decode(&v.BlockData, &ValueInfo{name: "BlockData", info: "压缩块"})
}

type Ebk2Header struct {
	BookId                 uint32
	HeadSize               uint16   `info:"信息头大小"`
	EbkVersion             uint16   `info:"文件版本"`
	EbkSize                uint32   `info:"文件大小"`
	BookName               BookName `info:"书名"`
	FileSize               uint32   `info:"原始文件大小"`
	HeadCompressSize       uint32   `info:"章节索引表压缩后大小"`
	FirstCompressBlockSize uint32   `info:"第一个压缩块大小"`
	ChapterCount           uint16   `info:"章节数量"`
	CompressBlockCount     uint16   `info:"压缩块数量"`
	MediaCount             uint32   `info:"媒体数量"`
	MediaLength            uint32   `info:"媒体数据长度"`
	TxtCompressSize        uint32   `info:"正文压缩后大小"`
}

type BookName string

func (v *BookName) Unmarshal(dec *Decoder, info *ValueInfo) {
	var str WString
	bs := dec.ReadBytes(64)
	NewDecoder(nil, bs, 0).Decode(&str, nil)
	*v = BookName(str)
}

type Ebk2HeaderData struct {
	Chapters []Ebk2Chapter
	Blocks   []Ebk2Block
}

func (v *Ebk2HeaderData) Unmarshal(dec *Decoder, info *ValueInfo) {
	ebk, ok := dec.Arg().(*Ebk2Book)
	if !ok || ebk == nil {
		panic("decode arg not set")
	}

	newDec := NewCompressDecoder(dec, int(ebk.Header.HeadCompressSize))

	v.Chapters = make([]Ebk2Chapter, ebk.Header.ChapterCount)
	v.Blocks = make([]Ebk2Block, ebk.Header.CompressBlockCount)
	newDec.Decode(&v.Chapters, &ValueInfo{name: "Chapters"})
	newDec.Decode(&v.Blocks, &ValueInfo{name: "Blocks"})
}

func NewCompressDecoder(dec *Decoder, size int) *Decoder {
	startPos := dec.Pos()
	buf := dec.ReadBytes(size)
	endPos := dec.Pos()
	cmp, err := zlib.NewReader(bytes.NewReader(buf))
	if err != nil {
		panic(err)
	}
	defer cmp.Close()
	buf, err = io.ReadAll(cmp)
	if err != nil {
		panic(err)
	}

	mapPosFunc := func(pos int) int {
		if pos == 0 {
			return startPos
		}
		return startPos + pos*(endPos-startPos)/len(buf)/8
	}

	itemFunc := dec.ItemAddFunc()
	return NewDecoder(func(start, end int, name, info, value string) {
		itemFunc(mapPosFunc(start), mapPosFunc(end), name, info, value)
	}, buf, 0)
}

type Ebk2Chapter struct {
	Name   BookName `info:"章节名"`
	Offset uint16   `info:"压缩块中章节地址"`
	Block  uint16   `info:"章节压缩块下标"`
	Size   uint32   `info:"解压后章节大小"`
}

func (v Ebk2Chapter) String() string {
	return string(v.Name)
}

type Ebk2Block struct {
	Offset uint32 `info:"压缩块文件地址"`
	Size   uint32 `info:"解压后压缩块大小"`
}

type Ebk2BlockData [][]byte

func (v *Ebk2BlockData) Unmarshal(dec *Decoder, info *ValueInfo) {
	ebk, ok := dec.Arg().(*Ebk2Book)
	if !ok || ebk == nil {
		panic("decode arg not set")
	}

	for i, b := range ebk.HeaderData.Blocks {
		var buf []byte
		if b.Offset != 0 {
			dec.Seek(int(b.Offset) * 8)
		}
		newDec := dec.SubDecoder(int(b.Size))
		newDec.Decode(&buf, &ValueInfo{name: strconv.Itoa(i)})
		dec.Seek(newDec.Pos())
		*v = append(*v, buf)
	}
}
